/** @format */

class Game {
    constructor({ data, drive, tetris }) {
        this.data = data
        this.drive = drive
        this.tetris = tetris
        // 定时器 ID
        this.timer = 0
        // 游戏状态：0: pending, 1: gaming, 2：paused, 3: gameover
        this.state = 0
        // 游戏等级，等级作为索引对应方块下落延迟，等级越高下落速度越快
        this.level = 1
        this.delays = [Infinity, 1000, 800, 600, 400, 200, 100]
        // 最高分、当前分
        this.topScore = 0
        this.curScore = 0
        // 记录当前方块“触底固定”后，所覆盖行能否消除
        this.rows = {}
    }
    // 更新计时器
    _setClock(callback, delay = 100) {
        // setInterval setTimeout RAF
        this.timer ? clearInterval(this.timer) : null
        this.timer = setInterval(callback, delay)
    }
    // 消行：原始重力实现
    // https://tetris.huijiwiki.com/wiki/%E6%B6%88%E8%A1%8C
    // 判断能否消行，将可消除行索引存储到 this.rows 中
    _canClear() {
        const rows = {}
        const data = this.data
        const pixels = this.tetris.present.pixels
        let [row, flag] = [0, false]
        for (let i = 0; i < 4; i++) {
            // 可被消除：true 不可消除：false
            row = pixels[i].y
            if (rows[row] === undefined) {
                data[row].forEach((e) => (!e ? (rows[row] = false) : null))
                rows[row] === undefined ? (rows[row] = flag = true) : null
            }
        }
        this.rows = rows
        return flag
    }
    // 单行数据下落
    _dropLine(step, start, end = 0) {
        for (let i = start; i >= end; i--) {
            for (let j = 0; j < this.data[0].length; j++) {
                this.data[i + step][j] = this.data[i][j]
            }
        }
    }
    // 游戏继续
    _gameOn() {
        // # 刷新方块
        this.tetris.reset()
        this.drive(this.data)
        // # 校准计时器
        this._setClock(this._getDrop(), this.delays[this.level])
    }
    // 执行多行消行，必须先调用 _canClear()
    _clearLine() {
        // console.log("执行消行", this)
        const cleari = []
        // const data = this.tetris._deepClone(this.data)
        const data = JSON.parse(JSON.stringify(this.data))
        // 获取可消除行数，置零临时 data 中的可消除行
        for (let row in this.rows) {
            if (this.rows[row]) {
                cleari.push(row)
                data[row].forEach((e, i) => (data[row][i] = 0))
            }
        }
        cleari.sort((a, b) => b - a)
        //闪烁可消除行：交替显示临时 data 与 this.data 3次
        let count = 0
        const start = Date.now()
        const delay = this.delays[this.level]
        this._setClock(() => {
            // console.log("消行闪烁", this, count, data, this.data)
            count % 2 ? this.drive(data) : this.drive(this.data)
            count++
            if (Date.now() - start >= delay) {
                // 下落调整
                // # 消 1 2 3 4 行
                // # 跨 1 2 行
                //  .  .  .  0   .  0
                //  .  .  0  0   0  .
                //  .  0  0  0   .  .
                //  0  0  0  0   0  0
                // 二消跨行判断
                if (cleari.length === 2) {
                    if (cleari[0] - 1 === cleari[1]) this._dropLine(2, cleari[1] - 1)
                    else {
                        cleari[0] - 2 === cleari[1]
                            ? this._dropLine(1, cleari[0] - 1, cleari[0] - 1)
                            : this._dropLine(1, cleari[0] - 1, cleari[0] - 2)
                        this._dropLine(2, cleari[1] - 1)
                    }
                } else this._dropLine(cleari.length, cleari[cleari.length - 1] - 1)
                // 游戏继续
                this._gameOn()
            }
        }, delay / 6)
    }
    // 判断游戏是否结束：消行后仍然触顶，必须先调用 _clearLine()
    _isOver() {
        let flag = false
        this.data[4].forEach((e) => (e ? (flag = true) : null))
        return flag
    }
    // 获取下坠回调
    _getDrop() {
        const that = this
        // Game Main
        // main(){
        //     this._setClock(this.main.bind(this))
        // }
        // main = () => {
        // this.setClock(this.main)
        // }
        return function () {
            // 如果当前方块仍能下坠（反之 locked）
            if (that.tetris.next("_toMove", "down")) {
                // 方块继续下坠
                that.tetris.move("down")
                that.drive(that.data)
            } else {
                // 方块 locked （触底固定）
                // # 先消行后判断
                // # 由于要通过定时器实现消行闪烁效果
                // # 不得不增加一层可消行判断
                // # 防止被消行后的 this._setClock() 覆盖
                if (that._canClear()) {
                    // 消行操作
                    that._clearLine()
                } else {
                    // 结束判断
                    if (that._isOver()) {
                        // 游戏结束
                        clearInterval(that.timer)
                        console.log("游戏结束")
                    } else {
                        // 游戏继续
                        that._gameOn()
                    }
                }
            }
        }
    }

    // 操控手柄：用户手动触发
    gamepad(key) {
        switch (key) {
            case "ArrowUp":
                if (this.state === 0) {
                    // Start Line ++
                } else if (this.state === 1 || this.state === 2) {
                    // 旋转
                    this.tetris.rotate("clockwise")
                    this.drive(this.data)
                    this.state = 1
                }
                break
            case "ArrowDown":
                if (this.state === 0) {
                    // Start Line --
                } else if (this.state === 1 || this.state === 2) {
                    // 下移
                    this.tetris.move("down")
                    this.drive(this.data)
                    this.state = 1
                }
                break
            case "ArrowLeft":
                if (this.state === 0) {
                    // Level --
                    this.level--
                } else if (this.state === 1 || this.state === 2) {
                    // 左移
                    this.tetris.move("left")
                    this.drive(this.data)
                    this.state = 1
                }
                break
            case "ArrowRight":
                if (this.state === 0) {
                    // Level ++
                    this.level++
                } else if (this.state === 1 || this.state === 2) {
                    // 右移
                    this.tetris.move("right")
                    this.drive(this.data)
                    this.state = 1
                }
                break
            case " ":
                // 下坠||开始
                if (this.state === 0) {
                    // 开始
                    this._setClock(this._getDrop(), this.delays[this.level])
                    this.state = 1
                } else if (this.state === 1 || this.state === 2) {
                    // 快速下坠
                    this._setClock(this._getDrop(), 8)
                    this.state = 1
                }
                break
            case "p":
                if (this.state === 0 || this.state === 2) {
                    // 恢复
                    this._setClock(this._getDrop(), this.delays[this.level])
                    this.state = 1
                } else if (this.state === 1) {
                    // 暂停
                    clearInterval(this.timer)
                    this.state = 2
                }
                break
            case "Enter":
                // 开始||重开
                if (this.state === 0) {
                    this.tetris.reset()
                    this.drive(data)
                    this._setClock(this._getDrop(), this.delays[this.level])
                    this.state = 1
                } else {
                }
                break
        }
    }

    // 单元测试：下落、触底、生成新方块
    // tetris.rotate("clockwise");drive(data)
    // tetris.move("left");drive(data)
    // tetris.move("right");drive(data)
    // tetris.move("down");drive(data)
    // tetris.flip("Y");drive(data)
    // tetris.flip("X");drive(data)
    // tetris.flip("diagonal");drive(data)
    // tetris.rotate("anticlockwise");drive(data)
    // tetris.reset();drive(data)
}
